package com.xiaotu.spider.yingbao.pageprocessor;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.xiaotu.common.util.FileUtil;
import com.xiaotu.common.util.GsonUtils;
import com.xiaotu.common.util.PageUtils;
import com.xiaotu.common.util.RegexUtils;
import com.xiaotu.common.util.SepratorUtil;
import com.xiaotu.common.util.StringUtil;
import com.xiaotu.spider.AUrlPageProcessor;

import us.codecraft.webmagic.Page;

/**
 * @类名 YingBaoPageProcessor
 * @日期 2017年7月25日
 * @作者 高海军
 * @功能
 */
@Component("YingBaoPageProcessor")
public class YingBaoPageProcessor extends AUrlPageProcessor
{
	private static final Logger LOGGER = LoggerFactory
			.getLogger(YingBaoPageProcessor.class);
	
	private static final int POOL_SIZE = 150000;
	
	private static final String ACTOR_ID = "actorId";
	
	private static final String[] BASE_FIELDS = new String[] { "id", ACTOR_ID,
			"actorName", "name", "mobile", "gender"/* 2为男，3为女 */, "workTime",
			"age", "birthDate", "height", "weight", "company", "intro",
			"school", "major", "schoolBeginDate", "schoolEndDate" };
	
	private static final String[] BASE_FIELDS_OTHER = new String[] {
			"headerUrl", "starSign", "userMobile" };
	
	private static final String[] LEVEL_FIELDS = new String[] { "nationality",
			"city", "nativePlace", "nation" };
	
	private static final String[] ARRAY_FIELDS = new String[] { "characterTags",
			"specialityTags" };
	
	private static final String[] WORK_FIELDS = new String[] { "id", "crewName",
			"crewType", "shootDate", "director", "actors", "roleLevelTag",
			"roleName", ACTOR_ID };
	
	private static final String[] IMAGE_FIELDS = new String[] { "id", "url",
			ACTOR_ID };
	
	private static final String[] AGENT_FIELDS = new String[] { "id", "name",
			"company" };
	private static final String[] AGENT_LEVEL_FIELDS = new String[] {
			"mobile" };
	
	private static String IMG_FILE;
	private static String ACTOR_FILE;
	private static String WORK_FILE;
	private static String AGENT_FILE;
	
	@Override
	public void initRunParams(Map<String, Object> map)
	{
		IMG_FILE = StringUtil.nullToString(map.get("imgFile"));
		ACTOR_FILE = StringUtil.nullToString(map.get("actorFile"));
		WORK_FILE = StringUtil.nullToString(map.get("workFile"));
		AGENT_FILE = StringUtil.nullToString(map.get("agentFile"));
	}
	
	@Override
	public void process(Page page)
	{
		String url = page.getUrl().toString();
		LOGGER.debug(url);
		if (url.endsWith("actor"))
			this.processActor(page);
		else
			this.processImages(page);
	}
	
	@SuppressWarnings("unchecked")
	private void processImages(Page page)
	{
		Map<String, Object> dataMap = this.getJsonMap(page, null);
		if (dataMap == null || dataMap.isEmpty())
			return;
		List<Map<String, Object>> dataList = (List<Map<String, Object>>) dataMap
				.get("data");
		if (dataList == null || dataList.isEmpty())
			return;
		List<Map<String, Object>> imageList = (List<Map<String, Object>>) dataList
				.get(0).get("images");
		if (imageList == null || imageList.isEmpty())
			return;
		String actorId = page.getRequest().getExtra(ACTOR_ID).toString();
		imageList.forEach(map ->
		{
			map.put(ACTOR_ID, actorId);
			FileUtil.writeFile(IMG_FILE,
					StringUtil.map2CSVLine(map, SepratorUtil.SEP_CSV_FIELDS,
							IMAGE_FIELDS) + SepratorUtil.LINE_SEPRATOR);
		});
	}
	
	@SuppressWarnings("unchecked")
	private Map<String, Object> getJsonMap(Page page, String key)
	{
		String json = page.getRawText();
		if (StringUtils.isEmpty(json))
			return null;
		Map<String, Object> map = GsonUtils.fromJson(json, Map.class);
		if (StringUtils.isEmpty(key))
			return map;
		return (Map<String, Object>) map.get(key);
	}
	
	@SuppressWarnings({ "unchecked" })
	private void processActor(Page page)
	{
		Map<String, Object> resMap = this.getJsonMap(page, "data");
		if (resMap == null || resMap.isEmpty())
			return;
		Map<String, Object> dataMap = (Map<String, Object>) resMap.get("actor");
		if (dataMap == null || dataMap.isEmpty())
			return;
		Map<String, Object> baseMap = this.getBaseInfo(dataMap);
		if (baseMap.get(BASE_FIELDS[0]) == null || baseMap.get(ACTOR_ID) == null
				|| StringUtils.isEmpty(
						StringUtil.nullToString(baseMap.get(BASE_FIELDS[2]))))
			return;
		String actorId = RegexUtils.regexSearch(RegexUtils.REGEX_INTEGER,
				baseMap.get(ACTOR_ID).toString());
		baseMap.put(ACTOR_ID, actorId);
		baseMap.putAll(this.getLevelVal(dataMap));
		baseMap.put(BASE_FIELDS_OTHER[0],
				getValueByParent(resMap, "user", "image", "url"));
		baseMap.put(BASE_FIELDS_OTHER[1], dataMap.get("zodiacTags") == null
				? null : ((List<String>) dataMap.get("zodiacTags")).get(0));
		baseMap.put(BASE_FIELDS_OTHER[2],
				getValueByParent(resMap, "user", "mobile"));
		
		Map<String, String[]> arrayMap = this.getArrayVal(dataMap);
		String[] occuption = dataMap.get("classifications") == null ? null
				: (((List<Map<String, Object>>) dataMap.get("classifications"))
						.stream()
						.map(labelMap -> labelMap.get("name").toString())
						.toArray(String[]::new));
		
		FileUtil.writeFile(ACTOR_FILE,
				StringUtil.map2CSVLine(baseMap, SepratorUtil.SEP_CSV_FIELDS,
						(String[]) ArrayUtils.addAll(ArrayUtils.addAll(
								BASE_FIELDS, BASE_FIELDS_OTHER), LEVEL_FIELDS))
						+ this.getArrayFields4CSV(arrayMap, ARRAY_FIELDS,
								occuption)
						+ SepratorUtil.SEP_CSV_FIELDS
						+ RegexUtils.regexSearch(RegexUtils.REGEX_INTEGER,
								page.getUrl().toString())
						+ SepratorUtil.LINE_SEPRATOR);
		
		this.processAgents(dataMap, actorId);
		this.addImagePage(page, actorId);
		this.processWorks(dataMap, actorId);
	}
	
	private String getArrayFields4CSV(Map<String, String[]> arrayMap,
			String[] arrayFields, String[] occuption)
	{
		StringBuilder buffer = new StringBuilder();
		Stream.of(arrayFields).forEach(key ->
		{
			List<Object> list = arrayMap == null ? null
					: (arrayMap.get(key) == null ? null
							: Arrays.asList(arrayMap.get(key)));
			buffer.append(
					SepratorUtil.SEP_CSV_FIELDS + this.getArrayFieldVal(list));
		});
		return buffer.toString() + SepratorUtil.SEP_CSV_FIELDS
				+ this.getArrayFieldVal(
						occuption == null ? null : Arrays.asList(occuption));
	}
	
	private String getArrayFieldVal(List<Object> list)
	{
		String val = StringUtil.list2CSVLine(list, SepratorUtil.SEP_CSV_ARRAY);
		if (StringUtils.isEmpty(val))
			return val;
		return "{" + val + "}";
	}
	
	@SuppressWarnings("unchecked")
	private void processAgents(Map<String, Object> dataMap, String actorId)
	{
		List<Map<String, Object>> agentList = (List<Map<String, Object>>) dataMap
				.get("agents");
		if (agentList == null || agentList.isEmpty())
			return;
		agentList.forEach(map ->
		{
			for (String field : AGENT_LEVEL_FIELDS)
				map.put(field, this.getValueByParent(map, "user", field));
			FileUtil.writeFile(AGENT_FILE,
					StringUtil.map2CSVLine(map, SepratorUtil.SEP_CSV_FIELDS,
							(String[]) ArrayUtils.addAll(AGENT_FIELDS,
									AGENT_LEVEL_FIELDS))
							+ SepratorUtil.SEP_CSV_FIELDS + actorId
							+ SepratorUtil.LINE_SEPRATOR);
		});
	}
	
	@SuppressWarnings("unchecked")
	private void processWorks(Map<String, Object> dataMap, String actorId)
	{
		List<Map<String, Object>> workList = (List<Map<String, Object>>) dataMap
				.get("experiences");
		if (workList == null || workList.isEmpty())
			return;
		workList.stream().filter(map -> map.get(WORK_FIELDS[1]) != null)
				.forEach(map ->
				{
					map.put(ACTOR_ID, actorId);
					map.put("roleLevelTags", map.get("roleLevelTags") != null
							? (((List<String>) map.get("roleLevelTags")).get(0))
							: StringUtils.EMPTY);
					FileUtil.writeFile(WORK_FILE,
							StringUtil.map2CSVLine(map,
									SepratorUtil.SEP_CSV_FIELDS, WORK_FIELDS)
									+ SepratorUtil.LINE_SEPRATOR);
				});
	}
	
	private Map<String, Object> getBaseInfo(Map<String, Object> dataMap)
	{
		Map<String, Object> baseMap = new HashMap<String, Object>();
		Stream.of(BASE_FIELDS).forEach(key -> baseMap.put(key,
				StringUtil.nullToString(dataMap.get(key))));
		return baseMap;
	}
	
	private Map<String, String> getLevelVal(Map<String, Object> dataMap)
	{
		Map<String, String> baseMap = new HashMap<String, String>();
		Stream.of(LEVEL_FIELDS).forEach(key -> baseMap.put(key,
				getValueByParent(dataMap, key, "name")));
		return baseMap;
	}
	
	@SuppressWarnings("unchecked")
	private Map<String, String[]> getArrayVal(Map<String, Object> dataMap)
	{
		Map<String, String[]> baseMap = new HashMap<String, String[]>();
		Stream.of(ARRAY_FIELDS).forEach(key -> baseMap.put(key,
				dataMap.get(key) == null ? null
						: (((List<String>) dataMap.get(key)).stream()
								.toArray(String[]::new))));
		return baseMap;
	}
	
	@SuppressWarnings("unchecked")
	private String getValueByParent(Map<String, Object> map, String... fields)
	{
		if (map == null)
			return StringUtil.nullToString(map);
		if (fields.length == 1)
			return StringUtil.nullToString(map.get(fields[0]));
		return getValueByParent((Map<String, Object>) map.get(fields[0]),
				Arrays.copyOfRange(fields, 1, fields.length));
	}
	
	private void addImagePage(Page page, String actorId)
	{
		page.getRequest().putExtra(ACTOR_ID, actorId);
		PageUtils
				.addTargetRequest(page,
						"https://api.yingbaowang.cn//api/Vnull/user/users/actors/"
								+ actorId + "/albums",
						page.getRequest().getExtras());
	}
	
	@Override
	protected String[] getStartRequestUrls()
	{
		int start = 110000;
		String[] urls = new String[POOL_SIZE - start + 1];
		for (int i = start; i <= POOL_SIZE; i++)
			urls[i - start] = "https://api.yingbaowang.cn//api/Vnull/user/"
					+ StringUtils.leftPad(i + StringUtils.EMPTY, 6, "0")
					+ "/actor";
		// return new String[] {
		// "https://api.yingbaowang.cn//api/Vnull/user/119329/actor" };
		return urls;
	}
	
	@Override
	protected String getSpiderType()
	{
		return "YingBao";
	}
	
	@Override
	public String getPageDataType(Page page)
	{
		return "Star";
	}
	
}
